﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using H2.Streams;
using H2.Streams.Sections;
using System.Reflection;
using System.Runtime.InteropServices;
using H2;

namespace Mystery
{
    public partial class DataShifter //Meta
    {
        public static int CreateMetaSpace(MapStream map, int shiftSize)
        {
            if (shiftSize <= 0) throw new Exception("Shift Size Must Be Greater Than Zero!");

            TagInfo ugh = map.Tags["ugh!", "i've got a lovely bunch of coconuts"];
            int insertPosition = ugh.Offset;

            PrepareForMetaShift(map, ref shiftSize);

            //Add Shift Size To Reflexive Offsets
            map.Position = ugh.Offset;
            ShiftBlock(map, typeof(H2.TagStructures.ugh_), shiftSize);

            //Move Data By Shift Size
            MoveData(map, ugh.Offset, shiftSize);

            //Update Ugh! TagInfo
            ugh.ChangeOffset(insertPosition + shiftSize, map.Index.SecondaryMagic);
            UpdateTagInfoOffset(map, ugh.ID, ugh.RawMetaOffset);

            return insertPosition;
        }

        private static void ShiftBlock(MapStream map, Type blockType, int shiftSize)
        {
            int blockStart = (int)map.Position;
            foreach (FieldInfo fi in blockType.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                Type type = fi.FieldType;
                if (!type.IsArray || type == typeof(byte[]) || type == typeof(char[])) continue;
                type = type.GetElementType();

                //Read Reflexive
                TagStructures.Reflexive reflexiveInfo = (TagStructures.Reflexive)type.GetCustomAttributes(typeof(TagStructures.Reflexive), false)[0];
                int chunkCount = map.ReadReflexiveAt(blockStart + reflexiveInfo.Offset);

                //Shift Sub Blocks
                int subBlockStart = (int)map.Position;
                for (int i = 0; i < chunkCount; i++)
                {
                    map.Position = subBlockStart + (i * reflexiveInfo.ChunkSize);
                    ShiftBlock(map, type, shiftSize);
                }

                //Add Shift Size To Reflexive Pointer
                int pointerOffset = blockStart + reflexiveInfo.Offset + 4;
                int pointer = map.ReadInt32At(pointerOffset, false);
                map.WriteAt(pointerOffset, shiftSize + pointer, false);
            }
        }

        private static void PrepareForMetaShift(MapStream map, ref int shiftSize)
        {
            const int PaddingSize = 2048;
            const int PaddingMask = PaddingSize - 1;
            //Add Padding To Shift Size
            if ((shiftSize & PaddingMask) != 0x0)
                shiftSize = (shiftSize & (~PaddingMask)) + PaddingSize;

            //Increase Map Size By Padded Shift Size
            IncreaseMapLength(map, shiftSize);
            map.Header.MetaSize += shiftSize;
            map.Header.NonRawSize += shiftSize;
        }

        //private void SkipValue(MapStream map, Type type)
        //{
        //    if (type == typeof(String))
        //        throw new Exception("String Skipping Not Implemented!");//(String)info2.GetValue(obj2)).Length;
        //    else if (type == typeof(Bitmask))
        //        throw new Exception("Bitmask Skipping Not Implemented!");//(Bitmask)info2.GetValue(obj2).ByteCountLength;
        //    else if (type == typeof(Dependancy))
        //        map.Position += 8;
        //    else if (type == typeof(StringID))
        //        map.Position += 4;
        //    else if (type != typeof(Enum))
        //        throw new Exception("Bitmask Skipping Not Implemented!");//(Enum)info2.GetValue(obj2)ByteCountLength;
        //    else map.Position += Marshal.SizeOf(type);
        //}
    }
}
